package MonteCarloMultiThreaded;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class MonteCarloMultiThreaded {
	public static double MonteCarloThreadOption(String callPutFlag, final double S, 
			final double X, double T, double r, double b,
			double v, final int nSteps, int nSimulations) throws InterruptedException, ExecutionException 
	{
		final int numberOfCores = Runtime.getRuntime().availableProcessors();
		final double blockingCoefficient = 0.9;
		final int poolSize = (int)(numberOfCores / (1 - blockingCoefficient));

		final double dt = T / nSteps;
		final double Drift = (b - v*v / 2) * dt;
		final double vSqrdt = v * java.lang.Math.sqrt(dt);

		final int z;

		if(callPutFlag.equals("c"))
			z = 1;
		else if(callPutFlag.equals("p"))
			z = -1;
		else
			return -1;

		final List<Callable<Double>> partitions = 
				new ArrayList<Callable<Double>>();

		for(int i=0;i<nSimulations;i++) {
			partitions.add(new Callable<Double>() {
				public Double call() throws Exception {
					java.util.Random random = new java.util.Random();
					double St = S;
					for ( int j=0 ; j < nSteps ; j++)
					{
						St = St * java.lang.Math.exp(Drift + vSqrdt * random.nextGaussian());
					}
				return Math.max(z * (St - X), 0);
				}
			});
		}

	final ExecutorService executorPool = 
			Executors.newFixedThreadPool(poolSize);    
	final List<Future<Double>> values = 
			executorPool.invokeAll(partitions, 10000, TimeUnit.SECONDS);
	
	double Sum = 0.0;

	for(final Future<Double> value : values)  
		Sum += value.get(); 

	executorPool.shutdown();
	return java.lang.Math.exp(-r * T) * (Sum / nSimulations);
	}

}
